﻿using System;
using System.Linq;
using System.Drawing;
using Microsoft.Win32;
using System.Xml.Linq;
using System.Collections;
using System.Windows.Forms;
using System.Globalization;
using System.ComponentModel;
using System.Custom.Drawing;
using System.Drawing.Drawing2D;
using System.Custom.Properties;
using System.Collections.Generic;
using System.Security.Permissions;
using System.Collections.ObjectModel;
using System.Runtime.InteropServices;

namespace System.Custom.Window
{
   public interface IVisualRegion
   {
      void Visualize();
      void Conceal();

      bool IsVisible
      {
         get;
      }
      ColorTheme ColorTheme
      {
         get;
         set;
      }
      ToolStripRenderMode RenderMode
      {
         get;
         set;
      }
   }

   public interface IInteractiveRegion : IVisualRegion
   {
      bool IsActive
      {
         get;
         set;
      }
   }

   public interface IWindow : IInteractiveRegion
   {
      bool IsAppActive
      {
         get;
      }

      event EventHandler<FlashWindowEventArgs> Flash;
      event EventHandler<ActivationEventArgs> Activation;
      event EventHandler<ActivationEventArgs> Deactivation;

      void StopFlash();
      bool FlashWindow(int count);
      bool FlashWindowOnce(bool invert);
      bool FlashIcon(Bitmap icon, int count);
      void FlashWindow(FlashType type, int count, Bitmap icon);

      void Localize(CultureInfo culture);
      void Translate(CultureInfo culture);
   }


   public enum FlashType
   {
      None,
      Icon,
      Window
   }

   public enum SlideAction
   {
      Switch,
      Expand,
      Collapse
   }

   public enum WindowsTheme
   {
      None,
      Blue,
      Olive,
      Silver,
      Unknown
   }

   public enum ActivationType
   {
      AppSelect,
      WindowSelect,
      MouseClick
   }

   public enum ColorFillStyle
   {
      None,
      Glass,
      Solid,
      Plastic,
      Metallic
   }

   public enum StatusGripStyle
   {
      Empty,
      Dotted,
      Modern,
      Classic
   }

   public enum ColorThemeColors
   {
      Control,
      MenuDark,
      MenuLight,
      AreaDark,
      AreaLight,
      PanelDark,
      PanelLight,
      HeaderDark,
      HeaderLight,
      ControlDark,
      AppMenuDark,
      AppMenuLight,
      PatternDark,
      PatternLight,
      ControlLight,
      ControlMiddle,
      BackgroundDark,
      BackgroundLight,
      BackgroundMiddle
   }

   public enum StatusBarDecoration
   {
      Empty,
      Borders,
      TopLine,
      TopShadow
   }


   public partial class Window : Form, IWindow
   {
      #region Window Data

      Icon FIcon;
      bool FIsFlashed;
      Icon FFlashedIcon;
      Timer FFlashTimer;
      FlashType FFlashType;
      bool FDisposeOnClose;
      ColorTheme FColorTheme;
      static bool FIsAppActive;
      static int FIsThemeActive = -1;
      FormWindowState FWindowState;
      protected CultureInfo FCulture;
      ToolStripRenderMode FRenderMode;
      protected bool FInteractiveState = true;
      protected ComponentResourceManager FResources;
      static WindowsTheme FWindowsTheme = WindowsTheme.Unknown;

      static ContextMenu contextMenuText;
      static TextBoxBase currentTextBox;
      static MenuItem menuItemTextUndo;
      static MenuItem menuItemSeparator1;
      static MenuItem menuItemTextCut;
      static MenuItem menuItemTextCopy;
      static MenuItem menuItemTextPaste;
      static MenuItem menuItemTextDelete;
      static MenuItem menuItemSeparator2;
      static MenuItem menuItemTextSelectAll;

      static int FSnapTimerLongInterval = 500;
      static int FSnapTimerShortInterval = 200;
      static int FSnapTimerAnimationInterval = 1;
      static System.Windows.Forms.Timer FSnapTimer;

      const int WA_ACTIVE = 1;
      const int WA_INACTIVE = 0;
      const int WA_CLICKACTIVE = 2;

      const int WM_KEYDOWN = 0x0100;
      const int WM_SYSCOMMAND = 0x112;
      const int WM_SYSKEYDOWN = 0x0104;
      const int WM_ACTIVATE = 0x0006;
      const int WM_MOUSEMOVE = 0x0200;
      const int WM_NCLBUTTONUP = 0xA2;
      const int WM_CONTEXTMENU = 0x7B;
      const int WM_ACTIVATEAPP = 0x001C;
      const int WM_NCLBUTTONDOWN = 0xA1;
      const int WM_MOUSEACTIVATE = 0x0021;
      const int WM_NCLBUTTONDBLCLK = 0xA3;

      #endregion

      #region Initialization

      static Window()
      {
         FSnapTimer = new System.Windows.Forms.Timer();
         FSnapTimer.Interval = FSnapTimerShortInterval;
         FSnapTimer.Tick += (s, e) => { DoSnapAnimation(FSnapTimer.Tag as Window); };

         contextMenuText = new System.Windows.Forms.ContextMenu();
         menuItemTextUndo = new System.Windows.Forms.MenuItem();
         menuItemSeparator1 = new System.Windows.Forms.MenuItem();
         menuItemTextCut = new System.Windows.Forms.MenuItem();
         menuItemTextCopy = new System.Windows.Forms.MenuItem();
         menuItemTextPaste = new System.Windows.Forms.MenuItem();
         menuItemTextDelete = new System.Windows.Forms.MenuItem();
         menuItemSeparator2 = new System.Windows.Forms.MenuItem();
         menuItemTextSelectAll = new System.Windows.Forms.MenuItem();

         contextMenuText.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] {
            menuItemTextUndo,
            menuItemSeparator1,
            menuItemTextCut,
            menuItemTextCopy,
            menuItemTextPaste,
            menuItemTextDelete,
            menuItemSeparator2,
            menuItemTextSelectAll});
         // 
         // menuItemTextUndo
         // 
         menuItemTextUndo.Index = 0;
         menuItemTextUndo.Text = System.Custom.Properties.Resources.S_Undo;
         menuItemTextUndo.Click += new System.EventHandler(menuItemTextUndo_Click);
         // 
         // menuItem13
         // 
         menuItemSeparator1.Index = 1;
         menuItemSeparator1.Text = "-";
         // 
         // menuItemTextCut
         // 
         menuItemTextCut.Index = 2;
         menuItemTextCut.Text = System.Custom.Properties.Resources.S_Cut;
         menuItemTextCut.Click += new System.EventHandler(menuItemTextCut_Click);
         // 
         // menuItemTextCopy
         // 
         menuItemTextCopy.Index = 3;
         menuItemTextCopy.Text = System.Custom.Properties.Resources.S_Copy;
         menuItemTextCopy.Click += new System.EventHandler(menuItemTextCopy_Click);
         // 
         // menuItemTextPaste
         // 
         menuItemTextPaste.Index = 4;
         menuItemTextPaste.Text = System.Custom.Properties.Resources.S_Paste;
         menuItemTextPaste.Click += new System.EventHandler(menuItemTextPaste_Click);
         // 
         // menuItemTextDelete
         // 
         menuItemTextDelete.Index = 5;
         menuItemTextDelete.Text = System.Custom.Properties.Resources.S_Delete;
         menuItemTextDelete.Click += new System.EventHandler(menuItemTextDelete_Click);
         // 
         // menuItem18
         // 
         menuItemSeparator2.Index = 6;
         menuItemSeparator2.Text = "-";
         // 
         // menuItemTextSelectAll
         // 
         menuItemTextSelectAll.Index = 7;
         menuItemTextSelectAll.Text = System.Custom.Properties.Resources.S_Select_all;
         menuItemTextSelectAll.Click += new System.EventHandler(menuItemTextSelectAll_Click);
      }
      public Window()
      {
         SnapBuffer = 20;
         ScreenSnap = true;
         FDisposeOnClose = false;
         FFlashTimer = new System.Windows.Forms.Timer();
         FFlashTimer.Tag = new Win32.FlashWindowInfo();
         FFlashTimer.Tick += new EventHandler(flashTimer_Tick);
         Shell.FileDialogVisibleChanged += OnFileDialogVisibleChanged;
         SystemEvents.UserPreferenceChanged += systemEvents_UserPreferenceChanged;
      }
      void InitializeComponent()
      {
         this.SuspendLayout();
         // 
         // Window
         // 
         this.ClientSize = new System.Drawing.Size(284, 264);
         this.Name = "Window";
         this.ResumeLayout(false);

      }
      protected override void Dispose(bool disposing)
      {
         if (disposing)
         {
            FFlashTimer.Dispose();
            Shell.FileDialogVisibleChanged -= OnFileDialogVisibleChanged;
            SystemEvents.UserPreferenceChanged -= systemEvents_UserPreferenceChanged;
         }
         base.Dispose(disposing);
      }

      #endregion

      #region IVisualRegion

      public virtual void Conceal()
      {
         Hide();
      }
      public virtual bool IsVisible
      {
         get { return Visible; }
      }
      public virtual void Visualize()
      {
         Show();
         if (WindowState == FormWindowState.Minimized) WindowState = FormWindowState.Normal;
         Activate();
         Update();
      }

      #endregion

      #region IInteractiveRegion

      public bool IsActive
      {
         get
         {
            return Form.ActiveForm == this;
         }
         set
         {
            if (value) Activate(); else WindowState = System.Windows.Forms.FormWindowState.Minimized;
         }
      }

      #endregion

      #region Events

      public event EventHandler NCLMouseUp;
      public event EventHandler NCLMouseDown;
      public event EventHandler NCLMouseDClick;
      public event CancelEventHandler SystemMenu;
      public event EventHandler WindowStateChanged;
      public event EventHandler WindowsThemeChanged;
      public event CancelEventHandler WindowContextMenu;
      public event EventHandler<FlashWindowEventArgs> Flash;
      public event EventHandler<ActivationEventArgs> Activation;
      public event EventHandler<ActivationEventArgs> Deactivation;
      public event EventHandler<SystemCommandEventArgs> SystemCommand;

      #endregion

      #region Members

      private Bitmap FlashedIcon
      {
         get
         {
            return FFlashedIcon.ToBitmap();
         }
         set
         {
            if (value != null)
            {
               value.MakeTransparent(Color.Magenta);
               FFlashedIcon = Icon.FromHandle(value.GetHicon());
            }
            else FFlashedIcon = null;
         }
      }

      public int SnapBuffer
      {
         get;
         set;
      }
      public bool ScreenSnap
      {
         get;
         set;
      }
      public bool IsAppActive
      {
         get { return FIsAppActive; }
      }
      public bool DisposeOnClose
      {
         get
         {
            return FDisposeOnClose;
         }
         set
         {
            FDisposeOnClose = value;
         }
      }
      public static bool IsThemeActive
      {
         get
         {
            try
            {
               if (FIsThemeActive < 0)
               {
                  RegistryKey key = Registry.CurrentUser.OpenSubKey("Software\\Microsoft\\Windows\\CurrentVersion\\ThemeManager");
                  FIsThemeActive = Convert.ToInt32(key.GetValue("ThemeActive", "1"));
               }
            }
            catch (Exception err)
            {
               //
            }
            return FIsThemeActive == 1;
         }
      }
      public virtual ColorTheme ColorTheme
      {
         get
         {
            return FColorTheme;
         }
         set
         {
            FColorTheme = value;
         }
      }
      public static WindowsTheme WindowsTheme
      {
         get
         {
            try
            {
               if (FWindowsTheme == WindowsTheme.Unknown)
               {
                  RegistryKey key = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\ThemeManager");
                  string colorName = key.GetValue("ColorName", "None").ToString();

                  FIsThemeActive = colorName == "None" ? 0 : 1;

                  if (colorName == "None") FWindowsTheme = WindowsTheme.None;
                  else if (colorName == "HomeStead") FWindowsTheme = WindowsTheme.Olive;
                  else if (colorName == "Metallic") FWindowsTheme = WindowsTheme.Silver;
                  else if (colorName == "NormalColor") FWindowsTheme = WindowsTheme.Blue;
               }
            }
            catch (Exception err)
            {
               MessageBox.Show(err.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Stop);
            }

            return FWindowsTheme;
         }
      }
      public virtual ToolStripRenderMode RenderMode
      {
         get
         {
            return FRenderMode;
         }
         set
         {
            FRenderMode = value;
         }
      }

      public void StopFlash()
      {
         FFlashTimer.Stop();
         if (FFlashedIcon != null)
         {
            Win32.DestroyIcon(FFlashedIcon.Handle);
            FFlashedIcon.Dispose();
         }
         if (FIcon != null) Icon = FIcon;
      }
      public bool FlashWindow(int count)
      {
         FlashWindow(FlashType.Window, count, null);

         FlashWindowOnce(true);

         return true;
      }
      public bool FlashWindowEx(int count)
      {
         Win32.FlashWindowInfo fwi = new Win32.FlashWindowInfo();

         fwi.hwnd = Handle;
         fwi.uCount = count;
         fwi.dwTimeout = 600;
         fwi.dwFlags = 0x00000003; // FLASHW_ALL
         fwi.cbSize = (int)Marshal.SizeOf(fwi);

         return Win32.FlashWindowEx(ref fwi);
      }
      public bool FlashWindowOnce(bool invert)
      {
         return Win32.FlashWindow(Handle, invert);
      }
      public bool FlashIcon(Bitmap icon, int count)
      {
         FlashWindow(FlashType.Icon, count, icon);

         Icon = FFlashedIcon;

         return true;
      }
      public void FlashWindow(FlashType type, int count, Bitmap icon)
      {
         FIcon = Icon;
         FFlashType = type;
         FlashedIcon = icon;

         Win32.FlashWindowInfo fwi = (Win32.FlashWindowInfo)FFlashTimer.Tag;
         fwi.dwFlags = 0;
         fwi.uCount = count * 2;

         FFlashTimer.Interval = 800;
         FFlashTimer.Tag = fwi;
         FFlashTimer.Start();
      }

      public virtual void Localize(CultureInfo culture)
      {
         if (FResources == null) FResources = new ComponentResourceManager(GetType());

         Localize(FResources, this, culture);
      }
      public virtual void Translate(CultureInfo culture)
      {
         if (FResources == null) FResources = new ComponentResourceManager(GetType());

         Translate(FResources, this, culture);
      }

      public static void ShowStandardTextMenu(TextBoxBase textBox)
      {
         currentTextBox = textBox;
         menuItemTextUndo.Enabled = !textBox.ReadOnly;
         menuItemTextCut.Enabled = !textBox.ReadOnly;
         menuItemTextPaste.Enabled = !textBox.ReadOnly;
         menuItemTextDelete.Enabled = !textBox.ReadOnly;
         contextMenuText.Show(textBox, textBox.PointToClient(Cursor.Position));
      }
      public static void DrawStatusBarDecoration(Graphics g, Rectangle r, StatusBarDecoration d)
      {
         if (d == StatusBarDecoration.Empty) return;

         r.X = 0; r.Y = 0;

         Pen pen = new Pen(Color.FromArgb(150, 150, 152));

         switch (d)
         {
            case StatusBarDecoration.Borders:
            {
               g.DrawLine(SystemPens.ControlDark, r.X, r.Y + 1, r.X, r.Y + r.Height - 1);
               g.DrawLine(SystemPens.ControlDark, r.X, r.Y + 1, r.X + r.Width - 1, r.Y + 1);
               g.DrawLine(SystemPens.ControlLightLight, r.X + r.Width - 1, r.Y, r.X + r.Width - 1, r.Y + r.Height - 1);
               g.DrawLine(SystemPens.ControlLightLight, r.X, r.Y + r.Height - 1, r.X + r.Width - 1, r.Y + r.Height - 1);
               break;
            }
            case StatusBarDecoration.TopShadow:
            {
               int dc = 25, yo = IsThemeActive ? 0 : 1;

               g.DrawLine(pen, r.X, r.Y + yo + 0, r.X + r.Width - 1, r.Y + yo + 0);

               pen.Color = Color.FromArgb(pen.Color.R + dc + 10, pen.Color.G + dc + 10, pen.Color.B + dc + 10);
               g.DrawLine(pen, r.X, r.Y + yo + 1, r.X + r.Width - 1, r.Y + yo + 1);

               pen.Color = Color.FromArgb(pen.Color.R + dc, pen.Color.G + dc, pen.Color.B + dc);
               g.DrawLine(pen, r.X, r.Y + yo + 2, r.X + r.Width - 1, r.Y + yo + 2);

               //pen.Color = Color.FromArgb(pen.Color.R + dc, pen.Color.G + dc, pen.Color.B + dc);
               //g.DrawLine(pen, r.X, r.Y + yo + 3, r.X + r.Width - 1, r.Y + yo + 3);

               break;
            }
            default:
            {
               pen.Color = Color.FromArgb(pen.Color.R + 10, pen.Color.G + 10, pen.Color.B + 10);
               g.DrawLine(pen, r.X, r.Y + 0, r.X + r.Width - 1, r.Y + 0);
               break;
            }
         }
         pen.Dispose();
      }
      public static void FillRectangle(Graphics g, Rectangle r, Color c1, Color c2, Color c3, LinearGradientMode gm)
      {
         Brush brush = null;
         ColorBlend blend = null;

         if (c1 != Color.Empty && c2 != Color.Empty && c3 != Color.Empty)
         {
            blend = new ColorBlend(3);
            blend.Colors = new Color[] { c1, c2, c3 };
            blend.Positions = new float[] { 0.0f, 0.50f, 1.0f };
            brush = new LinearGradientBrush(r, Color.Black, Color.Black, gm);
            (brush as LinearGradientBrush).InterpolationColors = blend;
         }
         else if (c1 != Color.Empty && c2 != Color.Empty)
         {
            blend = new ColorBlend(2);
            blend.Colors = new Color[] { c1, c2 };
            blend.Positions = new float[] { 0.0f, 1.0f };
            brush = new LinearGradientBrush(r, Color.Black, Color.Black, gm);
            (brush as LinearGradientBrush).InterpolationColors = blend;
         }
         else
         {
            brush = new SolidBrush(c1);
         }

         g.FillRectangle(brush, r);

         brush.Dispose();
      }
      public static void FillRectangle(Graphics g, Rectangle r, Color c1, Color c2, Color c3, Color c4, LinearGradientMode gm)
      {
         FillRectangle(g, r, c1, c2, c3, c4, 0.49f, 0.51f, 0.53f, 0.90f, gm);
      }
      public static void FillRectangle(Graphics g, Rectangle r, Color c1, Color c2, Color c3, Color c4, float p1, float p2, float p3, float p4, LinearGradientMode gm)
      {
         Brush brush = null;
         ColorBlend blend = null;

         blend = new ColorBlend(4);
         blend.Colors = new Color[] { c1, c2, c3, c3, c4, c4 };
         blend.Positions = new float[] { 0.0f, p1, p2, p3, p4, 1.0f };
         brush = new LinearGradientBrush(r, Color.Black, Color.Black, gm);
         (brush as LinearGradientBrush).InterpolationColors = blend;

         g.FillRectangle(brush, r);

         brush.Dispose();
      }
      public static void Localize(ComponentResourceManager resources, object target, CultureInfo culture)
      {
         // Order sensitive!!!

         if (target is Form)
         {
            Form form = target as Form;
            resources.ApplyResources(form, "$this", culture);
            foreach (Control c in form.Controls) Localize(resources, c, culture);
         }
         else if (target is MenuItem)
         {
            MenuItem item = target as MenuItem;
            resources.ApplyResources(item, item.Name, culture);
            foreach (MenuItem mi in item.MenuItems) Localize(resources, mi, culture);
         }
         else if (target is Menu)
         {
            Menu menu = target as Menu;
            foreach (MenuItem mi in menu.MenuItems) Localize(resources, mi, culture);
         }
         else if (target is ToolStrip)
         {
            ToolStrip toolStrip = target as ToolStrip;
            foreach (ToolStripItem tsi in toolStrip.Items) Localize(resources, tsi, culture);
         }
         else if (target is ToolStripDropDownItem)
         {
            ToolStripDropDownItem dropDownItem = target as ToolStripDropDownItem;
            resources.ApplyResources(dropDownItem, dropDownItem.Name, culture);
            foreach (ToolStripItem tsi in dropDownItem.DropDownItems) Localize(resources, tsi, culture);
         }
         else if (target is ToolStripDropDown)
         {
            ToolStripDropDown dropDown = target as ToolStripDropDown;
            resources.ApplyResources(dropDown, dropDown.Name, culture);
            foreach (ToolStripItem tsi in dropDown.Items) Localize(resources, tsi, culture);
         }
         else if (target is ToolStripItem)
         {
            ToolStripItem item = target as ToolStripItem;
            resources.ApplyResources(item, item.Name, culture);
         }
         else if (target is Label)
         {
            Label label = target as Label;
            resources.ApplyResources(label, label.Name, culture);
         }
         else if (target is ButtonBase)
         {
            ButtonBase btn = target as ButtonBase;
            resources.ApplyResources(btn, btn.Name, culture);
         }
         else if (target is Control)
         {
            Control control = target as Control;
            resources.ApplyResources(control, control.Name, culture);
            foreach (Control c in control.Controls) Localize(resources, c, culture);
         }
      }
      public static void Translate(ComponentResourceManager resources, object target, CultureInfo culture)
      {
         // Order sensitive!!!

         if (target is Form)
         {
            Form form = target as Form;
            form.Text = resources.GetString("$this.Text", culture);
            foreach (Control c in form.Controls) Translate(resources, c, culture);
         }
         else if (target is MenuItem)
         {
            MenuItem item = target as MenuItem;
            item.Text = resources.GetString(item.Name + ".Text", culture);
            foreach (MenuItem mi in item.MenuItems) Translate(resources, mi, culture);
         }
         else if (target is Menu)
         {
            Menu menu = target as Menu;
            foreach (MenuItem mi in menu.MenuItems) Translate(resources, mi, culture);
         }
         else if (target is ToolStrip)
         {
            ToolStrip toolStrip = target as ToolStrip;
            foreach (ToolStripItem tsi in toolStrip.Items) Translate(resources, tsi, culture);
         }
         else if (target is ToolStripDropDownItem)
         {
            ToolStripDropDownItem dropDownItem = target as ToolStripDropDownItem;
            dropDownItem.Text = resources.GetString(dropDownItem.Name + ".Text", culture);
            foreach (ToolStripItem tsi in dropDownItem.DropDownItems) Translate(resources, tsi, culture);
         }
         else if (target is ToolStripDropDown)
         {
            ToolStripDropDown dropDown = target as ToolStripDropDown;
            dropDown.Text = resources.GetString(dropDown.Name + ".Text", culture);
            foreach (ToolStripItem tsi in dropDown.Items) Translate(resources, tsi, culture);
         }
         else if (target is ToolStripItem)
         {
            ToolStripItem item = target as ToolStripItem;
            item.Text = resources.GetString(item.Name + ".Text", culture);
         }
         else if (target is Label)
         {
            Label label = target as Label;
            label.Text = resources.GetString(label.Name + ".Text", culture);
         }
         else if (target is ButtonBase)
         {
            ButtonBase btn = target as ButtonBase;
            btn.Text = resources.GetString(btn.Name + ".Text", culture);
         }
         else if (target is ComboBox || target is ListBox ||
                  target is TextBox || target is RichTextBox)
         {
            Control control = target as Control;
            resources.ApplyResources(control, control.Name, culture);
         }
         else if (target is Control)
         {
            Control control = target as Control;
            control.Text = resources.GetString(control.Name + ".Text", culture);
            foreach (Control c in control.Controls) Translate(resources, c, culture);
         }
      }

      protected virtual void UpdatewWindowView()
      {
         //
      }
      protected override void OnLocationChanged(EventArgs e)
      {
         if (FWindowState != WindowState)
         {
            if (WindowStateChanged != null) WindowStateChanged(this, e);

            FWindowState = WindowState;
         }
         base.OnLocationChanged(e);
      }
      protected override void OnFormClosing(FormClosingEventArgs e)
      {
         if (e.CloseReason == CloseReason.UserClosing)
         {
            if (FDisposeOnClose)
            {
               Dispose(true);
            }
            else
            {
               e.Cancel = true;
               Visible = false;
            }
            GC.Collect();
            Win32.SetProcessWorkingSetSize(Win32.GetCurrentProcess(), 5000000, 5000000);
         }
         base.OnFormClosing(e);
      }

      protected virtual void OnFileDialogVisibleChanged(object sender, EventArgs e)
      {
         Enabled = !Shell.FileDialogVisible;
      }

      private static void DoSnapAnimation(Window window)
      {
         if (!window.ScreenSnap ||
             window.WindowState != FormWindowState.Normal ||
             window.FormBorderStyle == FormBorderStyle.None)
         {
            FSnapTimer.Stop();
            return;
         }

         int screenWidth = Screen.PrimaryScreen.WorkingArea.Width;
         int screenHeight = Screen.PrimaryScreen.WorkingArea.Height;

         int dx = 0xFFFF, dy = 0xFFFF;

         if (window.Left <= 0) dx = window.Left == 0 || window.Left < -window.SnapBuffer ? 0 : -window.Left;
         else if (window.Left < window.SnapBuffer) dx = -1;
         else if (window.Left + window.Width >= screenWidth)
         {
            dx = window.Left + window.Width == screenWidth ||
                 window.Left + window.Width > screenWidth + window.SnapBuffer ? 0 : screenWidth - (window.Left + window.Width);
         }
         else if (screenWidth - (window.Left + window.Width) < window.SnapBuffer) dx = 1;

         if (window.Top <= 0) dy = window.Top == 0 || window.Top < -window.SnapBuffer ? 0 : -window.Top;
         else if (window.Top < window.SnapBuffer) dy = -1;
         else if (window.Top + window.Height >= screenHeight)
         {
            dy = window.Top + window.Height == screenHeight ||
                 window.Top + window.Height > screenHeight + window.SnapBuffer ? 0 : screenHeight - (window.Top + window.Height);
         }
         else if (screenHeight - (window.Top + window.Height) < window.SnapBuffer) dy = 1;

         if (dx == 0xFFFF && dy == 0xFFFF)
         {
            // Just moving window
         }
         else if (dx == 0 && dy == 0)
         {
            //FSnapTimer.Stop();
            FSnapTimer.Interval = FSnapTimerLongInterval;
         }
         else
         {
            Point mPos = Cursor.Position;
            window.Top += dy != 0xFFFF ? dy : 0;
            window.Left += dx != 0xFFFF ? dx : 0;
            mPos.Y += dy != 0xFFFF ? dy : 0;
            mPos.X += dx != 0xFFFF ? dx : 0;
            Cursor.Position = mPos;
            FSnapTimer.Interval = FSnapTimerAnimationInterval;
         }
      }

      private static void menuItemTextUndo_Click(object sender, EventArgs e)
      {
         currentTextBox.Undo();
      }
      private static void menuItemTextCut_Click(object sender, EventArgs e)
      {
         if (currentTextBox.ReadOnly) currentTextBox.Copy(); else currentTextBox.Cut();
      }
      private static void menuItemTextCopy_Click(object sender, EventArgs e)
      {
         currentTextBox.Copy();
      }
      private static void menuItemTextPaste_Click(object sender, EventArgs e)
      {
         if (!currentTextBox.ReadOnly) currentTextBox.Paste();
      }
      private static void menuItemTextDelete_Click(object sender, EventArgs e)
      {
         if (!currentTextBox.ReadOnly) currentTextBox.Text = currentTextBox.Text.Remove(currentTextBox.SelectionStart, currentTextBox.SelectionLength);
      }
      private static void menuItemTextSelectAll_Click(object sender, EventArgs e)
      {
         currentTextBox.SelectAll();
      }

      private void flashTimer_Tick(object sender, EventArgs e)
      {
         Win32.FlashWindowInfo fwi = (Win32.FlashWindowInfo)FFlashTimer.Tag;

         if (++fwi.dwFlags < fwi.uCount)
         {
            if (FFlashType == FlashType.Window)
            {
               FIsFlashed = FlashWindowOnce(true);
            }
            else if (FFlashType == FlashType.Icon)
            {
               Icon = Icon == FIcon ? FFlashedIcon : FIcon;

               FIsFlashed = Icon != FIcon;
            }
            else FIsFlashed = !FIsFlashed;

            int count = fwi.uCount - fwi.dwFlags - 1;

            if (Flash != null) Flash(this, new FlashWindowEventArgs(FIsFlashed, count));

            if (count == 0) FIsFlashed = false;
         }
         else
         {
            StopFlash();
         }

         FFlashTimer.Tag = fwi;
      }
      private void systemEvents_UserPreferenceChanged(object sender, UserPreferenceChangedEventArgs e)
      {
         if (e.Category == UserPreferenceCategory.VisualStyle)
         {
            FIsThemeActive = -1;
            FWindowsTheme = WindowsTheme.Unknown;
            if (WindowsThemeChanged != null) WindowsThemeChanged(sender, null);
         }
      }

      #endregion

      #region WndProc override

      [PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
      protected override void WndProc(ref System.Windows.Forms.Message message)
      {
         bool execDefaultWndProc = true;

         try
         {
            int activate = -1;
            ActivationType type = ActivationType.WindowSelect;

            switch (message.Msg)
            {
               case WM_KEYDOWN:
               {
                  break;
               }
               case WM_SYSKEYDOWN:
               {
                  break;
               }
               case WM_SYSCOMMAND:
               {
                  if (SystemCommand != null)
                  {
                     SystemCommandEventArgs a = new SystemCommandEventArgs((Win32.SystemCommand)((int)message.WParam & 0xFFF0), false);
                     SystemCommand(this, a);
                     execDefaultWndProc = !a.Cancel;
                  }
                  if (((int)message.WParam & 0xFFF0) == (int)Win32.SystemCommand.SC_MOUSEMENU)
                  {
                     if (SystemMenu != null)
                     {
                        CancelEventArgs a = new CancelEventArgs(false);
                        SystemMenu(this, a);
                        execDefaultWndProc = !a.Cancel;
                     }
                  }
                  break;
               }
               case WM_CONTEXTMENU:
               {
                  if (WindowContextMenu != null)
                  {
                     CancelEventArgs a = new CancelEventArgs(false);
                     WindowContextMenu(this, a);
                     execDefaultWndProc = !a.Cancel;
                  }
                  break;
               }
               case WM_NCLBUTTONUP:
               {
                  if (NCLMouseUp != null) NCLMouseUp(this, EventArgs.Empty);
                  break;
               }
               case WM_NCLBUTTONDOWN:
               {
                  if (ScreenSnap)
                  {
                     FSnapTimer.Tag = this;
                     FSnapTimer.Enabled = true;
                  }
                  if (NCLMouseDown != null) NCLMouseDown(this, EventArgs.Empty);
                  break;
               }
               case WM_NCLBUTTONDBLCLK:
               {
                  if (NCLMouseDClick != null) NCLMouseDClick(this, EventArgs.Empty);
                  break;
               }
               case WM_MOUSEMOVE:
               {
                  //
                  break;
               }
               case WM_ACTIVATEAPP:
               {
                  type = ActivationType.AppSelect;
                  activate = (int)message.WParam;
                  FIsAppActive = (activate != 0);
                  break;
               }
               case WM_MOUSEACTIVATE:
               {
                  type = ActivationType.MouseClick;
                  activate = 1;
                  break;
               }
               case WM_ACTIVATE:
               {
                  type = ActivationType.WindowSelect;

                  int param = Win32.LOWORD(message.WParam);

                  switch (param)
                  {
                     case WA_ACTIVE:
                     {
                        activate = 1;
                        break;
                     }
                     case WA_CLICKACTIVE:
                     {
                        type = ActivationType.MouseClick;
                        activate = 1;
                        break;
                     }
                     case WA_INACTIVE:
                     {
                        activate = 0;
                        break;
                     }
                  }
                  break;
               }
               case DWMMessages.WM_DWMCOMPOSITIONCHANGED:
               case DWMMessages.WM_DWMNCRENDERINGCHANGED:
               {
                  if (AeroGlassEffectChanged != null) AeroGlassEffectChanged.Invoke(this, EventArgs.Empty);
                  break;
               }
            }

            if (activate == 1)
            {
               if (Activation != null) Activation(this, new ActivationEventArgs(type));
            }
            else if (activate == 0)
            {
               if (Deactivation != null) Deactivation(this, new ActivationEventArgs(type));
            }
         }
         finally
         {
            if (execDefaultWndProc) base.WndProc(ref message);
         }
      }

      #endregion
   }


   public class ColorTheme
   {
      static ColorTheme FDefault;

      public readonly string Name;
      public readonly ColorFillStyle FillStyle;
      public readonly StatusGripStyle StatusGripStyle;
      public readonly StatusBarDecoration StatusBarDecoration;
      public readonly Dictionary<ColorThemeColors, Color> Colors = new Dictionary<ColorThemeColors, Color>();

      public Color this[ColorThemeColors color]
      {
         get
         {
            Color c = Color.Empty;
            return Colors.TryGetValue(color, out c) ? c : Color.White;
         }
         set
         {
            Colors[color] = value;
         }
      }

      public static ColorTheme ThemeXP
      {
         get
         {
            switch (Window.WindowsTheme)
            {
               case WindowsTheme.Blue: return BlueXP;
               case WindowsTheme.Olive: return OliveXP;
               case WindowsTheme.Silver: return SilverXP;
               default: return Default;
            }
         }
      }
      public static ColorTheme Default
      {
         get
         {
            return FDefault == null ? System : FDefault;
         }
         set
         {
            FDefault = value;
         }
      }
      public static readonly ColorTheme System;
      public static readonly ColorTheme Citron;
      public static readonly ColorTheme BlueXP;
      public static readonly ColorTheme OliveXP;
      public static readonly ColorTheme SilverXP;
      public static readonly ColorTheme Standard;
      public static readonly Color SilverLight = Color.FromArgb(223, 223, 227);
      public static readonly Color SilverLightLight = Color.FromArgb(236, 236, 240);
      public static readonly Color SearchBackgroundColor = Color.FromArgb(58, 110, 165);

      static ColorTheme()
      {
         System = new ColorTheme("System");
         Standard = new ColorTheme("Standard");
         BlueXP = new ColorTheme("BlueXP", XElement.Parse("<ColorTheme ControlLight=\"255 255 255\" Control=\"255 255 255\" ControlMiddle=\"255 255 255\" ControlDark=\"255 255 255\" />"));
         OliveXP = new ColorTheme("OliveXP", XElement.Parse("<ColorTheme ControlLight=\"252 252 254\" Control=\"244 243 238\" ControlMiddle=\"248 248 246\" ControlDark=\"244 243 238\" />"));
         SilverXP = new ColorTheme("SilverXP", XElement.Parse("<ColorTheme ControlLight=\"255 255 255\" Control=\"255 255 255\" ControlMiddle=\"255 255 255\" ControlDark=\"255 255 255\" />"));
         Citron = new ColorTheme("Citron", XElement.Parse("<ColorTheme ColorType=\"Glass\" ControlLight=\"250 251 240\" AreaLight=\"254 255 252\" Control=\"205 206 177\" ControlMiddle=\"254 255 210\" ControlDark=\"143 146 130\" StatusGripStyle=\"Empty\" StatusBarDecoration=\"Empty\" />"));
      }
      ColorTheme(string name)
      {
         Name = name;

         if (name == "Standard")
         {
            FillStyle = ColorFillStyle.Plastic;
            StatusGripStyle = StatusGripStyle.Classic;
            StatusBarDecoration = StatusBarDecoration.TopShadow;

            Colors[ColorThemeColors.Control] = SystemColors.Control;
            Colors[ColorThemeColors.MenuDark] = SystemColors.Control;
            Colors[ColorThemeColors.MenuLight] = SystemColors.ControlLightLight;
            Colors[ColorThemeColors.AreaDark] = SystemColors.ControlLightLight;
            Colors[ColorThemeColors.AreaLight] = Color.White;
            Colors[ColorThemeColors.PanelDark] = SystemColors.ControlDark;
            Colors[ColorThemeColors.PanelLight] = SystemColors.ControlLightLight;
            Colors[ColorThemeColors.HeaderDark] = Color.White;
            Colors[ColorThemeColors.HeaderLight] = Color.White;
            Colors[ColorThemeColors.ControlDark] = SystemColors.Control;
            Colors[ColorThemeColors.AppMenuDark] = SystemColors.Control;
            Colors[ColorThemeColors.AppMenuLight] = Color.White;
            Colors[ColorThemeColors.PatternDark] = SystemColors.Control;
            Colors[ColorThemeColors.PatternLight] = Color.White;
            Colors[ColorThemeColors.ControlLight] = SystemColors.ControlLightLight;
            Colors[ColorThemeColors.ControlMiddle] = SystemColors.ControlLight;
            Colors[ColorThemeColors.BackgroundDark] = SystemColors.ControlDark;
            Colors[ColorThemeColors.BackgroundLight] = SystemColors.Control;
            Colors[ColorThemeColors.BackgroundMiddle] = SystemColors.Control;
         }
         else
         {
            FillStyle = ColorFillStyle.Solid;
            StatusGripStyle = StatusGripStyle.Empty;
            StatusBarDecoration = StatusBarDecoration.Empty;

            Colors[ColorThemeColors.Control] = SystemColors.Control;
            Colors[ColorThemeColors.MenuDark] = SystemColors.Control;
            Colors[ColorThemeColors.MenuLight] = SystemColors.Control;
            Colors[ColorThemeColors.AreaDark] = SystemColors.ControlLight;
            Colors[ColorThemeColors.AreaLight] = SystemColors.ControlLight;
            Colors[ColorThemeColors.PanelDark] = SystemColors.ControlLightLight;
            Colors[ColorThemeColors.PanelLight] = SystemColors.ControlLightLight;
            Colors[ColorThemeColors.HeaderDark] = Color.White;
            Colors[ColorThemeColors.HeaderLight] = Color.White;
            Colors[ColorThemeColors.ControlDark] = SystemColors.Control;
            Colors[ColorThemeColors.AppMenuDark] = SystemColors.Control;
            Colors[ColorThemeColors.AppMenuLight] = SystemColors.Control;
            Colors[ColorThemeColors.PatternDark] = SystemColors.ControlLightLight;
            Colors[ColorThemeColors.PatternLight] = SystemColors.ControlLightLight;
            Colors[ColorThemeColors.ControlLight] = SystemColors.Control;
            Colors[ColorThemeColors.ControlMiddle] = SystemColors.Control;
            Colors[ColorThemeColors.BackgroundDark] = SystemColors.ControlDark;
            Colors[ColorThemeColors.BackgroundLight] = SystemColors.ControlDark;
            Colors[ColorThemeColors.BackgroundMiddle] = SystemColors.ControlDark;
         }
      }
      public ColorTheme(string name, XElement xTheme)
      {
         Name = name;

         XAttribute xColorType = xTheme.Attribute("ColorType");
         XAttribute xStatusGripStyle = xTheme.Attribute("StatusGripStyle");
         XAttribute xStatusBarDecoration = xTheme.Attribute("StatusBarDecoration");

         FillStyle = xColorType == null ? ColorFillStyle.Plastic : (ColorFillStyle)Enum.Parse(typeof(ColorFillStyle), xColorType.Value);
         StatusGripStyle = xStatusGripStyle == null ? StatusGripStyle.Empty : (StatusGripStyle)Enum.Parse(typeof(StatusGripStyle), xStatusGripStyle.Value);
         StatusBarDecoration = xStatusBarDecoration == null ? StatusBarDecoration.Empty : (StatusBarDecoration)Enum.Parse(typeof(StatusBarDecoration), xStatusBarDecoration.Value);

         Array colorsId = Enum.GetValues(typeof(ColorThemeColors));

         foreach (ColorThemeColors colorId in colorsId)
         {
            XAttribute xColor = xTheme.Attribute(colorId.ToString());

            if (xColor == null)
            {
               Colors[colorId] = Color.Empty;
            }
            else
            {
               if (xColor.Value.Contains(" ") || xColor.Value.Contains(",") || xColor.Value.Contains(";"))
               {
                  string[] rgb = xColor.Value.Split(' ', ',', ';');
                  Colors[colorId] = Color.FromArgb(Convert.ToInt16(rgb[0]), Convert.ToInt16(rgb[1]), Convert.ToInt16(rgb[2]));
               }
               else
               {
                  Colors[colorId] = Color.FromName(xColor.Value);
               }
            }
         }
         foreach (ColorThemeColors colorId in colorsId)
         {
            if (Colors[colorId] == Color.Empty)
            {
               if (colorId == ColorThemeColors.BackgroundLight) Colors[colorId] = Colors[ColorThemeColors.Control];
               else if (colorId == ColorThemeColors.BackgroundMiddle) Colors[colorId] = Colors[ColorThemeColors.Control];
               else if (colorId == ColorThemeColors.BackgroundDark) Colors[colorId] = Colors[ColorThemeColors.ControlDark];
               else if (colorId == ColorThemeColors.AreaDark) Colors[colorId] = Colors[ColorThemeColors.ControlLight];
               else if (colorId == ColorThemeColors.AreaLight) Colors[colorId] = Colors[ColorThemeColors.ControlLight];
               else if (colorId == ColorThemeColors.PanelDark) Colors[colorId] = Colors[ColorThemeColors.Control];
               else if (colorId == ColorThemeColors.PanelLight) Colors[colorId] = Colors[ColorThemeColors.ControlLight];
               else if (colorId == ColorThemeColors.PatternDark) Colors[colorId] = Colors[ColorThemeColors.Control];
               else if (colorId == ColorThemeColors.PatternLight) Colors[colorId] = Colors[ColorThemeColors.ControlLight];
               else if (colorId == ColorThemeColors.AppMenuLight) Colors[colorId] = Colors[ColorThemeColors.ControlLight];
               else if (colorId == ColorThemeColors.AppMenuDark) Colors[colorId] = Colors[ColorThemeColors.Control];

               if (Colors[colorId] == Color.Empty) Colors[colorId] = Standard[colorId];
            }
         }
      }
      public ColorTheme(string name, ColorTheme theme)
      {
         Name = name;

         FillStyle = theme.FillStyle;
         StatusGripStyle = theme.StatusGripStyle;
         StatusBarDecoration = theme.StatusBarDecoration;

         foreach (var color in theme.Colors) Colors[color.Key] = color.Value;
      }

      public override string ToString()
      {
         return Name;
      }
   }

   public class MenuElement
   {
      string FText;
      Image FImage;
      object FData;
      bool FChecked;
      bool FIsSeparator;
      EventHandler FOnClick;

      public string Text
      {
         get
         {
            return FText;
         }
      }
      public Image Image
      {
         get
         {
            return FImage;
         }
      }
      public object Data
      {
         get
         {
            return FData;
         }
      }
      public bool Checked
      {
         get
         {
            return FChecked;
         }
      }
      public bool IsSeparator
      {
         get
         {
            return FIsSeparator;
         }
      }
      public EventHandler OnClick
      {
         get
         {
            return FOnClick;
         }
      }

      public MenuElement()
      {
         FIsSeparator = true;
      }
      public MenuElement(string text, bool isChecked, Image image, object data, EventHandler click)
      {
         FText = text;
         FData = data;
         FImage = image;
         FOnClick = click;
         FChecked = isChecked;
      }
   }

   public class MenuWrapper
   {
      public enum AnimationEffect
      {
         None,
         Fade,
      }

      bool FIsVisible;
      bool FUseVisualStyles;
      Image FBackgroundImage;
      ContextMenu FMenuWin32;
      ContextMenuStrip FMenuStrip;
      ToolStripRenderMode FRenderMode;
      Point FMenuPos = new Point(0, 0);
      CancelEventHandler FMenuOpening;
      Collection<MenuElement> FElements = new Collection<MenuElement>();
      Collection<Component> FSourceControls = new Collection<Component>();

      static double FMaxMenuOpacity = 1.0;
      static ContextMenuStrip FCurrentMenu;
      static System.Windows.Forms.Timer FTimerFader;

      public static void ShowMenu(ContextMenuStrip menu)
      {
         bool leftAlign = Cursor.Position.X + menu.Width + 10 > Screen.PrimaryScreen.WorkingArea.Width;
         bool topAlign = Cursor.Position.Y + menu.Height + 10 > Screen.PrimaryScreen.WorkingArea.Height;

         int x = Cursor.Position.X - (leftAlign ? menu.Width : 0);
         int y = Cursor.Position.Y - (topAlign ? menu.Height : 0);

         ShowMenu(menu, x, y);
      }
      public static void ShowMenu(ContextMenuStrip menu, int x, int y)
      {
         if (FCurrentMenu == menu) return;

         FCurrentMenu = menu;
         FCurrentMenu.OwnerItem = null;
         FCurrentMenu.Opacity = 0.0;
         FCurrentMenu.Show(x, y);
         FTimerFader.Start();
      }
      public static void MakeFadeEffect(ContextMenuStrip menu, double maxOpacity)
      {
         FMaxMenuOpacity = maxOpacity;

         if (FCurrentMenu == menu) return;

         FCurrentMenu = menu;
         FCurrentMenu.Opacity = 0.0;
         FCurrentMenu.Show();
         FTimerFader.Start();
      }

      static MenuWrapper()
      {
         FTimerFader = new System.Windows.Forms.Timer();

         FTimerFader.Interval = 1;

         FTimerFader.Tick += (s, e) =>
         {
            if (FCurrentMenu == null || !FCurrentMenu.Visible)
            {
               FCurrentMenu = null;
               FTimerFader.Stop();
               return;
            }

            FCurrentMenu.Opacity += 0.14;

            if (FCurrentMenu.Opacity > 0.93)
            {
               FCurrentMenu.Opacity = FMaxMenuOpacity;
               FCurrentMenu = null;
            }
         };
      }
      public MenuWrapper()
      {
         CreateContextMenu();
      }
      public MenuWrapper(Component source, EventHandler[] handlers)
      {
         AssignSource(source, handlers);
      }

      private void Destroy()
      {
         if (FMenuStrip != null) FMenuStrip.Dispose();
         if (FMenuWin32 != null) FMenuWin32.Dispose();
         FMenuStrip = null;
         FMenuWin32 = null;
      }
      private void RegenerateMenu(bool styles)
      {
         Destroy();

         if (styles)
         {
            CreateContextMenuStrip();
         }
         else
         {
            CreateContextMenu();
         }

         foreach (MenuElement element in FElements) Add(element);
         foreach (Component control in FSourceControls) SetSourceControl(control);
      }

      private void CreateContextMenu()
      {
         FMenuWin32 = new ContextMenu();
      }
      private void CreateContextMenuStrip()
      {
         FMenuStrip = new ContextMenuStrip();
         FMenuStrip.RenderMode = FRenderMode;
         FMenuStrip.BackgroundImage = FBackgroundImage;
         FMenuStrip.BackgroundImageLayout = ImageLayout.Stretch;
         FMenuStrip.Opening += FMenuOpening;
      }
      private void SetSourceControl(Component control)
      {
         if (control is ToolStripDropDownItem)
         {
            UseVisualStyles = true;
            (control as ToolStripDropDownItem).DropDown = FMenuStrip;
         }
         else if (control is ToolBarButton)
         {
            UseVisualStyles = false;
            (control as ToolBarButton).DropDownMenu = FMenuWin32;
         }
         else if (control is Menu)
         {
            UseVisualStyles = false;
            (control as Menu).MenuItems.Clear();
            foreach (MenuItem item in FMenuWin32.MenuItems)
            {
               (control as Menu).MenuItems.Add(item);
            }
         }
      }

      public bool IsVisible
      {
         get
         {
            if (FMenuWin32 != null)
            {
               return FIsVisible;
            }
            else if (FMenuStrip != null)
            {
               return FMenuStrip.Visible;
            }
            return false;
         }
      }
      public bool UseVisualStyles
      {
         get
         {
            return FUseVisualStyles;
         }
         set
         {
            if (FUseVisualStyles == value) return;

            RegenerateMenu(value);
            FUseVisualStyles = value;
         }
      }
      public Image BackgroundImage
      {
         get
         {
            return FBackgroundImage;
         }
         set
         {
            FBackgroundImage = value;
            if (FMenuStrip != null) FMenuStrip.BackgroundImage = value;
         }
      }
      public CancelEventHandler MenuOpening
      {
         get
         {
            return FMenuOpening;
         }
         set
         {
            if (FMenuStrip == null) return;

            FMenuStrip.Opening -= FMenuOpening;
            FMenuOpening = value;
            FMenuStrip.Opening += FMenuOpening;
         }
      }
      public ToolStripRenderMode RenderMode
      {
         get
         {
            return FRenderMode;
         }
         set
         {
            FRenderMode = value;
            if (FMenuStrip != null) FMenuStrip.RenderMode = value;
         }
      }

      public void Clear()
      {
         Destroy();
         FElements.Clear();
         if (FUseVisualStyles) CreateContextMenuStrip(); else CreateContextMenu();
      }
      public void AddSeparator()
      {
         Add(new MenuElement());
      }
      public void Remove(object data)
      {
         if (FMenuWin32 != null)
         {
            foreach (MenuItem item in FMenuWin32.MenuItems)
            {
               if (item.Tag == data) { item.Dispose(); break; }
            }
         }
         else if (FMenuStrip != null)
         {
            foreach (ToolStripItem item in FMenuStrip.Items)
            {
               if (item.Tag == data) { item.Dispose(); break; }
            }
         }

         foreach (MenuElement element in FElements)
         {
            if (element.Data == data) { FElements.Remove(element); break; }
         }
      }
      public void Add(MenuElement element)
      {
         Insert(-1, element);
      }
      public void AddSourceControl(Component control)
      {
         SetSourceControl(control);
         if (!FSourceControls.Contains(control)) FSourceControls.Add(control);
      }
      public void Show(Control control, int x, int y)
      {
         FMenuPos.X = x;
         FMenuPos.Y = y;

         if (FMenuWin32 != null)
         {
            FIsVisible = true;
            FMenuWin32.Show(control, FMenuPos);
            FIsVisible = false;
         }
         else if (FMenuStrip != null)
         {
            FMenuStrip.Show(control, FMenuPos);
         }
      }
      public void Insert(int index, MenuElement element)
      {
         if (FMenuWin32 != null)
         {
            if (element.IsSeparator)
            {
               FMenuWin32.MenuItems.Add("-");
            }
            else
            {
               MenuItem item = new MenuItem(element.Text, element.OnClick);
               if (index > FMenuWin32.MenuItems.Count - 1) index = FMenuWin32.MenuItems.Count - 1;
               if (index < 0) FMenuWin32.MenuItems.Add(item);
               else FMenuWin32.MenuItems.Add(index, item);
               item.Tag = element.Data;
               item.Checked = element.Checked;
               if (element.Image != null)
               {
                  IntPtr hBitmap = ((Bitmap)element.Image).GetHbitmap();
                  bool b = Win32.SetMenuItemBitmaps(item.Handle, (uint)item.Index, (uint)0x00000400L, hBitmap, hBitmap);
               }
            }
         }
         else if (FMenuStrip != null)
         {
            if (element.IsSeparator)
            {
               FMenuStrip.Items.Add(new ToolStripSeparator());
            }
            else
            {
               ToolStripMenuItem item = new ToolStripMenuItem(element.Text, element.Image, element.OnClick);
               if (index > FMenuStrip.Items.Count - 1) index = FMenuStrip.Items.Count - 1;
               if (index < 0) FMenuStrip.Items.Add(item); else FMenuStrip.Items.Insert(index, item);
               item.Tag = element.Data;
               item.Checked = element.Checked;
               item.ImageScaling = ToolStripItemImageScaling.None;
            }
         }

         if (!FElements.Contains(element)) FElements.Add(element);
      }
      public void Add(string text, Image image, object data, EventHandler click)
      {
         Add(new MenuElement(text, false, image, data, click));
      }
      public void Add(string text, bool isChecked, object data, EventHandler click)
      {
         Add(new MenuElement(text, isChecked, null, data, click));
      }
      public void Insert(int index, string text, Image image, object data, EventHandler click)
      {
         Insert(index, new MenuElement(text, false, image, data, click));
      }
      public void Insert(int index, string text, bool isChecked, object data, EventHandler click)
      {
         Insert(index, new MenuElement(text, isChecked, null, data, click));
      }
      public void AssignSource(Component source, EventHandler[] handlers)
      {
         IEnumerator hndEnum = handlers.GetEnumerator();

         FElements.Clear();

         if (source is ContextMenu)
         {
            foreach (MenuItem item in (source as ContextMenu).MenuItems)
            {
               Add(item.Text, null, item.Tag, (EventHandler)hndEnum.Current);
               hndEnum.MoveNext();
            }
         }
         else if (source is ContextMenuStrip)
         {
            foreach (ToolStripItem item in (source as ContextMenuStrip).Items)
            {
               Add(item.Text, item.Image, item.Tag, (EventHandler)hndEnum.Current);
               hndEnum.MoveNext();
            }
         }
      }
   }

   public static class ControlEffects
   {
      public static void Slide(Control control, SlideAction action, bool animate)
      {
         Slide(control, action, true, animate, null, null);
      }
      public static void Slide(Control control, SlideAction action, bool refresh, bool animate, Action<SlideAction> beforeAnimate, Action<SlideAction> afterAnimate)
      {
         if (control.Tag != null) return;

         int minHeight = control.MinimumSize.Height;
         int maxHeight = control.MaximumSize.Height;

         if (maxHeight == 0) return;

         int delta = (maxHeight - minHeight) / 8;

         if (delta < 1) animate = false;

         ScrollableControl baseControl = control.Parent as ScrollableControl;
         Size autoScrollMinSize = baseControl != null ? baseControl.AutoScrollMinSize : new Size();

         Action<SlideAction> completeAnimation = delegate(SlideAction a)
         {
            control.Tag = null;

            if (baseControl != null)
            {
               baseControl.AutoScrollMinSize = autoScrollMinSize;
               baseControl.ScrollControlIntoView(control);
               if (refresh) baseControl.Refresh();
            }

            if (animate && afterAnimate != null) afterAnimate(a);
         };

         if (control.Height > minHeight)
         {
            if (action == SlideAction.Expand) return;

            action = SlideAction.Collapse;

            if (animate)
            {
               if (beforeAnimate != null) beforeAnimate(action);

               Timer t = new Timer();
               t.Interval = 1;

               control.Tag = "1";

               t.Tick += (s, e) =>
               {
                  if (control.Height > minHeight + delta)
                  {
                     control.Height -= delta;
                     autoScrollMinSize.Height -= delta;
                  }
                  else
                  {
                     t.Dispose();
                     control.Height = minHeight;
                     autoScrollMinSize.Height = minHeight;
                     completeAnimation(action);
                  }
               };
               t.Start();
            }
            else
            {
               control.Height = minHeight;
               autoScrollMinSize.Height = minHeight;
               completeAnimation(action);
            }
         }
         else
         {
            if (action == SlideAction.Collapse) return;

            action = SlideAction.Expand;

            if (animate)
            {
               if (beforeAnimate != null) beforeAnimate(action);

               control.Tag = "1";

               Timer t = new Timer();
               t.Interval = 1;

               t.Tick += (s, e) =>
               {
                  if (control.Height < maxHeight - delta)
                  {
                     control.Height += delta;
                     autoScrollMinSize.Height += delta;
                  }
                  else
                  {
                     t.Dispose();
                     control.Height = maxHeight;
                     autoScrollMinSize.Height = maxHeight;
                     completeAnimation(action);
                  }
               };
               t.Start();
            }
            else
            {
               control.Height = maxHeight;
               autoScrollMinSize.Height = maxHeight;
               completeAnimation(action);
            }
         }
      }
   }

   public class ActivationEventArgs : EventArgs
   {
      public readonly ActivationType ActivationType;

      public ActivationEventArgs(ActivationType t)
      {
         ActivationType = t;
      }
   }

   public class FlashWindowEventArgs : EventArgs
   {
      public bool Active;

      public int RemainTimes;

      public FlashWindowEventArgs(bool active, int remainTimes)
      {
         Active = active;

         RemainTimes = remainTimes;
      }
   }

   public class SystemCommandEventArgs : CancelEventArgs
   {
      public readonly Win32.SystemCommand Command;

      public SystemCommandEventArgs(Win32.SystemCommand cmd, bool cancel)
         : base(cancel)
      {
         Command = cmd;
      }
   }
}
